/* General Purpose Timer example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/timer.h"
#include "esp_log.h"
#include "ina226.h"
#include "nvs_blob.h"
#define TIMER_DIVIDER         (16)  //  Hardware timer clock divider
#define TIMER_SCALE           (TIMER_BASE_CLK / TIMER_DIVIDER)  // convert counter value to seconds
#define timer_soc_print_switch 1
int task_delaynum=0;
uint32_t time_S = 0;//记录运行时间
static const char *TAG = "timer_soc";
/*实验+数据拟合的420mAh电池参数*/
//最小二乘拟合函数（冬季室内10度左右，天气预报温度-6℃）：SOC = a + B*OCV + C*OCV.^2
float a = 7.34;//OCV-SOC拟合函数的常数项
float b = -5.01;//OCV-SOC拟合函数的一次项系数
float c = 0.85;//OCV-SOC拟合函数的二次项系数
float R0 = 3;//欧姆内阻，实际范围3~4（冬季）
float OCV = 0;//开路电压
float OCV_SOC = 0;//根据OCV-SOC函数计算的SOC值
/*SOC参数*/
uint32_t Q0 = 3000*60*60;//电池容量ma*秒
float SOC0 = 1;//初始SOC
float SOCt = 1;//当前SOC
float SOCr = 0;//已放掉的SOC
float I_sumf = 0;//已释放的总电流
uint32_t I_sum_u32 = 0;//已释放的总电流
float Eta = 1;//库伦效率，假设电池已循环次数很少
uint32_t delta_t = 1;//一秒采样一次
extern float ina226_Current,ina226_bus_vol;//ina226读取的电流值、端电压值
xQueueHandle soc_queue;//与lvgl_begin传递soc消息队列
/*定时器参数结构体*/
typedef struct {
    int timer_group;
    int timer_idx;
    int alarm_interval;
    bool auto_reload;
} example_timer_info_t;

/**
 * @brief A sample structure to pass events from the timer ISR to task
 *
 */
typedef struct {
    example_timer_info_t info;
    uint64_t timer_counter_value;
} example_timer_event_t;

static xQueueHandle s_timer_queue;

/*
 * A simple helper function to print the raw timer counter value
 * and the counter value converted to seconds
 */
static void inline print_timer_counter(uint64_t counter_value)
{
    printf("Counter: 0x%08x%08x\r\n", (uint32_t) (counter_value >> 32),
           (uint32_t) (counter_value));
    printf("Time   : %.8f s\r\n", (double) counter_value / TIMER_SCALE);
}

static bool IRAM_ATTR timer_group_isr_callback(void *args)
{
    BaseType_t high_task_awoken = pdFALSE;
    example_timer_info_t *info = (example_timer_info_t *) args;

    uint64_t timer_counter_value = timer_group_get_counter_value_in_isr(info->timer_group, info->timer_idx);

    /* Prepare basic event data that will be then sent back to task */
    example_timer_event_t evt = {
        .info.timer_group = info->timer_group,
        .info.timer_idx = info->timer_idx,
        .info.auto_reload = info->auto_reload,
        .info.alarm_interval = info->alarm_interval,
        .timer_counter_value = timer_counter_value
    };

    if (!info->auto_reload) {
        timer_counter_value += info->alarm_interval * TIMER_SCALE;
        timer_group_set_alarm_value_in_isr(info->timer_group, info->timer_idx, timer_counter_value);
    }
    /*若是TIMER_GROUP_0，则进一次中断task_delaynum-1*/
    if (info->timer_group == TIMER_GROUP_0)
    {
        //ina226获取的电流应实时带入计算，否则电流实时变化的情况下容量计算不准
        I_sumf = I_sumf + Eta*ina226_Current*delta_t;//进入中断一次统计一次放电量
        task_delaynum++;
       //printf("timer 3s \n"); //!IDF规定中断函数内不能有printf
    }
    /* Now just send the event data back to the main program task */
    //!中断中必须使用带有中断保护功能的 xQueueSendFromISR(),不能使用xQueueSend
    xQueueSendFromISR(s_timer_queue, &evt, &high_task_awoken);//用于向队列尾部发送一个队列消息,如果队列满，函数在high_task_awoken时间内返回
    return high_task_awoken == pdTRUE; // return whether we need to yield at the end of ISR
}

/**
 * @brief Initialize selected timer of timer group
 *
 * @param group Timer Group number, index from 0
 * @param timer timer ID, index from 0
 * @param auto_reload whether auto-reload on alarm event
 * @param timer_interval_sec interval of alarm
 */
static void example_tg_timer_init(int group, int timer, bool auto_reload, int timer_interval_sec)
{
    /* Select and initialize basic parameters of the timer */
    timer_config_t config = {
        .divider = TIMER_DIVIDER,
        .counter_dir = TIMER_COUNT_UP,
        .counter_en = TIMER_PAUSE,
        .alarm_en = TIMER_ALARM_EN,
        .auto_reload = auto_reload,
    }; // default clock source is APB
    timer_init(group, timer, &config);

    /* Timer's counter will initially start from value below.
       Also, if auto_reload is set, this value will be automatically reload on alarm */
    timer_set_counter_value(group, timer, 0);

    /* Configure the alarm value and the interrupt on alarm. */
    timer_set_alarm_value(group, timer, timer_interval_sec * TIMER_SCALE);
    timer_enable_intr(group, timer);

    example_timer_info_t *timer_info = calloc(1, sizeof(example_timer_info_t));
    timer_info->timer_group = group;
    timer_info->timer_idx = timer;
    timer_info->auto_reload = auto_reload;
    timer_info->alarm_interval = timer_interval_sec;
    timer_isr_callback_add(group, timer, timer_group_isr_callback, timer_info, 0);

    timer_start(group, timer);
}
uint32_t FloatToU32(float dat)
{
	uint8_t buf[4];
	
	buf[0] = ((uint8_t*)&dat)[0];
	buf[1] = ((uint8_t*)&dat)[1];
	buf[2] = ((uint8_t*)&dat)[2];
	buf[3] = ((uint8_t*)&dat)[3];
	
	return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
}

float U32ToFloat(uint32_t dat)
{
	uint8_t buf[4];
	
	buf[0] = dat >> 24;
	buf[1] = dat >> 16;
	buf[2] = dat >> 8;
	buf[3] = dat & 0xff;
	
	return *((float*)buf);
}


void soc_test(void)
{
    soc_queue = xQueueCreate(1, sizeof(soc_queue));
    s_timer_queue = xQueueCreate(1, sizeof(example_timer_event_t));
    example_tg_timer_init(TIMER_GROUP_0, TIMER_0, true, 1);//使用TIMER_GROUP_0作为任务切换的定时器
    example_tg_timer_init(TIMER_GROUP_1, TIMER_0, false, 5);
    //!第一种方法只考虑安时积分，未考虑端电压，且电池初始SOC未知
    // esp_err_t err = nvs_flash_init();
    // err = Read_Isum(&I_sum_u32);//读取nvs中复位/断电前已存的电流释放值
    // I_sumf =  U32ToFloat(I_sum_u32);
    // if (err != ESP_OK) 
    //     printf("Error (%s) reading data from NVS!\n", esp_err_to_name(err));
    // else
    //     printf("reading data from NVS:%d ma\n",I_sum_u32);
    //!第二种方法：根据Thevenin模型计算开路电压，查SOC-OCV表确定初始SOC值
    //由于SOC在20%以下时SOC-OCV曲线变化较大，因此不考虑20%以下的SOC估计
    while (1) {
        example_timer_event_t evt;
        //用于从一个队列中接收消息并把消息从队列中删除
        xQueueReceive(s_timer_queue, &evt, portMAX_DELAY);//队列容量为1，中断一次就填满，main函数就能立刻接收到数据evt
        if (task_delaynum == 2) //2秒打印一次SOC值
        {
            
            // I_sum_u32 = FloatToU32(I_sumf);
            // err = Save_Isum(&I_sum_u32);//将已释放的电流量存储到nvs中，防止ESP32断电/复位后重置I_sum为0
            // if (err != ESP_OK) printf("Error (%s) saving restart counter to NVS!\n", esp_err_to_name(err));
            time_S +=task_delaynum;
            SOCr = I_sumf/Q0;
            SOCt = SOC0-SOCr;
            task_delaynum = 0;
            /* OCV-SOC法 */
            OCV = ina226_bus_vol/1000 + ina226_Current*R0;//mV单位换算为V单位
            OCV_SOC = a + b*OCV + c*OCV*OCV;//根据最小二乘拟合系数（冬季0度左右的测试值）计算OCV对应SOC值
            xQueueSend(soc_queue,&OCV_SOC,0);
            if (timer_soc_print_switch)
            {
                // printf("Current  = %f \n",ina226_Current);
                ESP_LOGI(TAG,"OCV  = %f \n",OCV);
                // printf("ina226_bus_vol = %f\n",ina226_bus_vol);
                ESP_LOGI(TAG,"OCV_SOC = %f \n",OCV_SOC);
                // printf("Running Time = %d S\n",time_S);
            }           
        }
        vTaskDelay(pdMS_TO_TICKS(100));             
    }
    vTaskDelete(NULL);//正常不会执行到这里
}
